﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kokomo.Lexical.Expressions;
using System.Reflection;

namespace Kokomo.PeInspector.Reflection
{
	static class CSharpOperatorOverloads
	{
		static CSharpOperatorOverloads()
		{
			InitializeOperatorOverloadTables();
		}

		private static Dictionary<string, ExpressionOperator> BinaryOperatorOverloadTable { get; set; }
		private static Dictionary<string, ExpressionOperator> UnaryOperatorOverloadTable { get; set; }
		public static readonly string ImplicitOverloadName = "op_Implicit";
		public static readonly string ExplicitOverloadName = "op_Explicit";

		private static void InitializeOperatorOverloadTables()
		{
			Dictionary<string, ExpressionOperator> binaryTable = new Dictionary<string, ExpressionOperator>();
			binaryTable["op_Addition"] = ExpressionOperator.Addition;
			binaryTable["op_Subtraction"] = ExpressionOperator.Subtraction;
			binaryTable["op_Multiply"] = ExpressionOperator.Multiplication;
			binaryTable["op_Division"] = ExpressionOperator.Division;
			binaryTable["op_Modulus"] = ExpressionOperator.Modulus;
			binaryTable["op_BitwiseAnd"] = ExpressionOperator.BitwiseAnd;
			binaryTable["op_BitwiseOr"] = ExpressionOperator.BitwiseOr;
			binaryTable["op_ExclusiveOr"] = ExpressionOperator.Exponential;
			binaryTable["op_LeftShift"] = ExpressionOperator.ShiftLeft;
			binaryTable["op_RightShift"] = ExpressionOperator.ShiftRight;
			binaryTable["op_Equality"] = ExpressionOperator.Equality;
			binaryTable["op_Inequality"] = ExpressionOperator.Inequality;
			binaryTable["op_LessThan"] = ExpressionOperator.LessThan;
			binaryTable["op_LessThanOrEqual"] = ExpressionOperator.LessThanOrEqualTo;
			binaryTable["op_GreaterThan"] = ExpressionOperator.GreaterThan;
			binaryTable["op_GreaterThanOrEqual"] = ExpressionOperator.GreaterThanOrEqualTo;
			BinaryOperatorOverloadTable = binaryTable;

			Dictionary<string, ExpressionOperator> unaryTable = new Dictionary<string, ExpressionOperator>();
			unaryTable["op_UnaryPlus"] = null;
			unaryTable["op_UnaryNegation"] = ExpressionOperator.Negation;
			unaryTable["op_LogicalNot"] = ExpressionOperator.LogicalNot;
			unaryTable["op_OnesComplement"] = ExpressionOperator.BitwiseComplement;
			unaryTable["op_Increment"] = ExpressionOperator.Increment;
			unaryTable["op_Decrement"] = ExpressionOperator.Decrement;
			unaryTable["op_True"] = null;
			unaryTable["op_False"] = ExpressionOperator.LogicalNot;
			UnaryOperatorOverloadTable = unaryTable;
		}

		public static bool IsBinaryOperatorOverload(MethodInfo method)
		{
			if (method == null) throw new ArgumentNullException("method");
			return BinaryOperatorOverloadTable.ContainsKey(method.Name);
		}
		public static bool IsUnaryOperatorOverload(MethodInfo method)
		{
			if (method == null) throw new ArgumentNullException("method");

			bool isUnary = UnaryOperatorOverloadTable.ContainsKey(method.Name)
				|| (method.Name == ImplicitOverloadName)
				|| (method.Name == ExplicitOverloadName)
				;
			return isUnary;
		}
		public static Expression GetExpression(MethodInfo overloadMethod, Expression left, Expression right)
		{
			if (!IsBinaryOperatorOverload(overloadMethod)) throw new ArgumentException(Messages.NotBinaryOverloadMethodMessage, "overloadMethod");
			if (left == null) throw new ArgumentNullException("left");
			if (right == null) throw new ArgumentNullException("right");

			ExpressionOperator op = GetBinaryOperator(overloadMethod);
			Expression expression = new BinaryOperationExpression(left, right, op, overloadMethod);
			return expression;
		}

		public static ExpressionOperator GetBinaryOperator(MethodInfo overloadMethod)
		{
			ExpressionOperator op = BinaryOperatorOverloadTable[overloadMethod.Name];
			return op;
		}
		public static Expression GetExpression(MethodInfo overloadMethod, Expression operand)
		{
			if (!IsUnaryOperatorOverload(overloadMethod)) throw new ArgumentException(Messages.NotUnaryOverloadMethodMessage, "overloadMethod");
			if (operand == null) throw new ArgumentNullException("operand");

			if (overloadMethod.Name == ImplicitOverloadName) return operand;
			else if (overloadMethod.Name == ExplicitOverloadName)
			{
				Type targetType = overloadMethod.ReturnType;
				CastExpression cast = new CastExpression(operand, targetType);
				return cast;
			}
			else
			{
				ExpressionOperator op = GetUnaryOperator(overloadMethod);
				Expression expression = new UnaryOperationExpression(operand, op, overloadMethod);
				return expression;
			}
		}

		public static ExpressionOperator GetUnaryOperator(MethodInfo overloadMethod)
		{
			ExpressionOperator op = UnaryOperatorOverloadTable[overloadMethod.Name];
			return op;
		}
	}
}
